package com.douya.pangu.application.sys.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.douya.common.enums.CommonResultEnums;
import com.douya.common.ex.ApiException;
import com.douya.pangu.application.basic.mapper.CoreUserMapper;
import com.douya.pangu.application.basic.pojo.entity.CoreUserDO;
import com.douya.pangu.application.basic.service.CoreUserService;
import com.douya.pangu.application.sys.pojo.vo.LoginUserVO;
import com.douya.security.property.DouYaSecurityProperties;
import com.douya.security.property.HeaderTokenProperties;
import com.douya.security.utils.JwtUtils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务
 *
 * @Author hcy
 * @Date 2022/11/10
 */
@Slf4j
@Service
@Transactional(readOnly = true)
public class UserService {

    @Resource
    CoreUserMapper mCoreUserMapper;

    BCryptPasswordEncoder mBCryptPasswordEncoder = new BCryptPasswordEncoder(10);

    @Resource
    JwtUtils jwtUtils;

    @Resource
    RedissonClient mRedissonClient;

    @Resource
    DouYaSecurityProperties securityProperties;

    @Resource
    CoreUserService mCoreUserService;

    /**
     * 说明：登录
     *
     * @param loginName 登录名
     * @param password  密码
     * @return 登录用户信息
     */
    public LoginUserVO login(String loginName, String password) {
        CoreUserDO userDO = mCoreUserMapper.selectOne(
                new QueryWrapper<CoreUserDO>()
                        .lambda().eq(CoreUserDO::getLoginName, loginName)
        );
        if (Objects.isNull(userDO)) {
            throw new ApiException("账号不存在或密码错误", CommonResultEnums.INVALID_PARAMS.getCode());
        }
        boolean matches = mBCryptPasswordEncoder.matches(password, userDO.getLoginPassword());
        if (!matches) {
            throw new ApiException("账号不存在或密码错误", CommonResultEnums.INVALID_PARAMS.getCode());
        }
        return getLoginUser(userDO);
    }

    /**
     * 说明：注册
     *
     * @param loginName 用户名
     * @param password  用户密码
     */
    @Transactional(rollbackFor = Exception.class)
    public void register(String loginName, String password) {
        CoreUserDO coreUser = mCoreUserMapper.selectOne(
                new QueryWrapper<CoreUserDO>()
                        .lambda().eq(CoreUserDO::getLoginName, loginName)
        );
        if (Objects.nonNull(coreUser)) {
            throw new ApiException("账户名已存在", CommonResultEnums.INVALID_PARAMS.getCode());
        }
        String encode = mBCryptPasswordEncoder.encode(password);
        CoreUserDO userDO = new CoreUserDO();
        userDO.setLoginName(loginName)
                .setUserName(loginName)
                .setLoginPassword(encode);
        mCoreUserMapper.insert(userDO);
    }

    /**
     * 说明：修改密码
     *
     * @param loginName   用户名
     * @param oldPassword 旧密码
     * @param newPassword 新密码
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePassword(String loginName, String oldPassword, String newPassword) {
        CoreUserDO coreUser = mCoreUserMapper.selectOne(
                new QueryWrapper<CoreUserDO>()
                        .lambda().eq(CoreUserDO::getLoginName, loginName)
        );
        if (Objects.isNull(coreUser)) {
            throw new ApiException("账号不存在", CommonResultEnums.INVALID_PARAMS.getCode());
        }
        boolean matches = mBCryptPasswordEncoder.matches(oldPassword, coreUser.getLoginPassword());
        if (!matches) {
            throw new ApiException("密码错误", CommonResultEnums.INVALID_PARAMS.getCode());
        }
        String encode = mBCryptPasswordEncoder.encode(newPassword);
        coreUser.setLoginPassword(encode);
        mCoreUserMapper.updateById(coreUser);
    }

    /**
     * 获取登录用户信息
     *
     * @param userDO 用户信息
     * @return 登录用户信息
     */
    private LoginUserVO getLoginUser(CoreUserDO userDO) {
        LoginUserVO loginUser = new LoginUserVO();
        String loginName = userDO.getLoginName();
        String loginPassword = userDO.getLoginPassword();
        Long id = userDO.getId();
        List<String> permissionList = mCoreUserService.userPermissionList(id);
        Map<String, Object> claims = Maps.newHashMap();
        claims.put("id", id);
        claims.put("subject", loginPassword);
        String token = jwtUtils.generateToken(claims);
        loginUser.setLoginName(loginName)
                .setId(id)
                .setToken(token)
                .setPermissionList(permissionList);
        HeaderTokenProperties headerToken = securityProperties.getHeaderToken();
        boolean multiLogin = securityProperties.isMultiLogin();
        // multiLogin 为 true 时，支持多端登录，为 false 时，不支持多端登录
        if (multiLogin) {
            // 支持多端登录
            String tokenKey = headerToken.getTokenPrefix() + token;
            RBucket<Object> bucket = mRedissonClient.getBucket(tokenKey);
            bucket.set(id, headerToken.getExpireTime().toMinutes(), TimeUnit.MINUTES);
        } else {
            // 不支持多端登录
            String tokenKey = headerToken.getTokenPrefix() + id;
            RBucket<Object> bucket = mRedissonClient.getBucket(tokenKey);
            boolean exists = bucket.isExists();
            if (exists) {
                bucket.delete();
            }
            bucket.set(token, headerToken.getExpireTime().toMinutes(), TimeUnit.MINUTES);
        }
        return loginUser;
    }

}
